﻿using System;
using System.Collections.Generic;
using System.Text;

namespace CANMonitor
{
    //CRC16
    public class crc16
    {
        /* Based on the polynomial x^16+x^12+x^5+1 */
        static UInt16[] CrcCcittTable = new UInt16[256]
        {
            0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
            0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
            0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
            0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
            0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
            0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
            0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
            0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
            0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
            0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
            0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
            0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
            0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
            0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
            0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
            0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
            0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
            0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
            0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
            0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
            0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
            0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
            0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
            0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
            0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
            0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
            0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
            0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
            0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
            0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
            0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
            0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
        };

        static public UInt16 GetCRCCode(byte[] pBuf, int iLen)
        {
            UInt32 i;
            UInt16 j, crc = 0;
            for( i = 0; i < iLen; i++ )
            {
                j = (UInt16)((crc >> 8) ^ pBuf[i]);
                crc = (UInt16)((crc << 8) ^ CrcCcittTable[j]);
            }
            return crc;
        }
    }

    //组帧
    public class mt_link_frame_rx_t 
    {
		public static int Cnt_rx_bytes;
		public static int Cnt_crc_errors;
		public static int Cnt_length_errors;
		public static int Cnt_discard_bytes;
		public static int Cnt_rx_frames;

		public static void Clear_Cnts()
		{
			Cnt_rx_bytes = 0;
			Cnt_crc_errors = 0;
			Cnt_length_errors = 0;
			Cnt_discard_bytes = 0;
			Cnt_rx_frames = 0;
		}
        //---------------------------------------------------------
        const UInt16 MAX_DATA_SIZE = 1024;
        const UInt16 MAX_FRAME_SIZE = (MAX_DATA_SIZE + 9);
        enum link_rx_state_t
        {
            STFrameRx_head,
            STFrameRx_addr,
            STFrameRx_cmd,
            STFrameRx_datalen,
            STFrameRx_data,
        }
        //---------------------------------------------------------

        link_rx_state_t state;
		UInt32 tick = 0;

        byte addr;
        byte cmd;
	    UInt16 data_len;
        byte[] frame = new byte[MAX_FRAME_SIZE];
		byte[] last = new byte[3];

        UInt16 cnt;

        //---------------------------------------------------------

		private UInt16 U8_To_U16(byte l, byte h)
		{
			UInt16 val = h; val <<= 8; val += l;
			return val;
		}

        public link_msg_t link_make_frame(byte data_in)
        {
            link_msg_t msg = null;
	        UInt32 flag_error = 0;

			Cnt_rx_bytes++;//诊断信息
	        if (this.cnt > MAX_FRAME_SIZE)
	        {
				Cnt_length_errors = 0; //诊断信息
				Cnt_discard_bytes += this.cnt;

				this.state = link_rx_state_t.STFrameRx_head;
		        this.cnt = 0;
	        }

			this.last[0] = this.last[1]; this.last[1] = this.last[2]; this.last[2] = data_in;
			if (this.last[0] == 0xAA && this.last[1] == 0x55 && this.last[2] == 0xBB)
			{
				if (this.cnt != 0) { Cnt_discard_bytes += this.cnt; } //诊断信息

				this.frame[0] = this.last[0]; this.frame[1] = this.last[1]; this.frame[2] = this.last[2];
				this.cnt = 3;
				this.state = link_rx_state_t.STFrameRx_addr;
				this.tick = MillisecondTimer.Ticks;
				return null;
			}
        	
	        switch(this.state)
	        {
                case link_rx_state_t.STFrameRx_head: // 0 1 2
		        break;

                case link_rx_state_t.STFrameRx_addr: // 3
			        if (this.cnt >= 4) {flag_error = 1; break;}//追求效率时,屏蔽此检查
        			
			        this.addr  = this.frame[this.cnt++] = data_in;
					this.state = link_rx_state_t.STFrameRx_cmd;
		        break;

                case link_rx_state_t.STFrameRx_cmd: // 4
			        if (this.cnt >= 5) {flag_error = 1; break;}//追求效率时,屏蔽此检查
        			
			        this.cmd   = this.frame[this.cnt++] = data_in;
					this.state = link_rx_state_t.STFrameRx_datalen;
		        break;

                case link_rx_state_t.STFrameRx_datalen: // 5 6
			        if (this.cnt >= 7) {flag_error = 1; break;}//追求效率时,屏蔽此检查
        				
			        this.frame[this.cnt++] = data_in;
			        if (this.cnt == 7)
			        {
				        this.data_len = U8_To_U16(this.frame[5],this.frame[6]);
				        if (this.data_len > MAX_DATA_SIZE)
				        {
							Cnt_length_errors++; //诊断信息
					        flag_error = 1;
					        break;
				        }
						this.state = link_rx_state_t.STFrameRx_data;
			        }
		        break;

                case link_rx_state_t.STFrameRx_data:// 7 8 this.data_len+8
			        if (this.cnt >= (this.data_len+9)) {flag_error = 1; break;}//追求效率时,屏蔽此检查
        			
			        this.frame[this.cnt++] = data_in;
			        if (this.cnt == this.data_len+9)
			        {
				        UInt16 crc = crc16.GetCRCCode(this.frame, this.cnt - 2);
						UInt16 crc_rx = U8_To_U16(this.frame[this.cnt - 2], this.frame[this.cnt - 1]);
						if (crc == crc_rx)
						{
							msg = new link_msg_t();
							msg.LinkAddr = (link_addr_t)this.addr;
							msg.LinkCmd = (link_cmd_ack_t)this.cmd;
							msg.data_len = this.data_len;
							msg.data = new byte[this.data_len];
							for (int i = 0; i < this.data_len; i++)
								msg.data[i] = this.frame[7 + i];
							Cnt_rx_frames++;//诊断信息
						}
						else
						{
							Cnt_crc_errors++;
						}
						this.state = link_rx_state_t.STFrameRx_head;
				        this.cnt = 0;
			        }
		        break;
        		
		        default:
			        flag_error = 1;
		        break;
	        }

	        if (flag_error != 0)
	        {
				Cnt_discard_bytes += this.cnt; //诊断信息
				this.state = link_rx_state_t.STFrameRx_head;
				this.cnt = 0;
	        }

            return msg;
        }

        //---------------------------------------------------------
    }

    public enum link_addr_t
    {
        LinkAddr_ECU = 0x01,//ECU（主控）
        LinkAddr_MC = 0x02,//MC（电控）
        LinkAddr_BMS = 0x03,//BMS（电池）
        LinkAddr_HEL = 0x04,//HEL（头盔锁）
        LinkAddr_PS = 0x05,//PS（压力传感器）
        LinkAddr_LT = 0x06,//LT（龙头）
    }

    public enum link_cmd_ack_t
    {
        //升级开始，0xA1
        LinkCmd_A1_Upgrade_Start = 0xA1,
        LinkAck_A1_AllowUpgrade = 0x00,//0x00：可以升级
        linkAck_A1_FirmewareErr = 0x01,//0x01：固件错误
        LinkAck_A1_MemNotEnough = 0x02,//0x02：空间不足
        LinkAck_A1_NoAPP = 0x04,//0x04：无APP

        //升级数据发送，0xA2
        LinkCmd_A2_Upgrade_Data = 0xA2,
        LinkAck_A2_BuffedSuccess = 0x00,//0x00：写入成功
        LinkAck_A2_BuffedFailed = 0x01,//0x01：写入失败
        LinkAck_A2_IdRepeated = 0x02,//0x02：此id已写过
        LinkAck_A2_FirstPackErr = 0x03,//0x03: 固件首帧数据错误
        LinkAck_A2_IdError = 0x04,

        //升级结束，0xA3
        LinkCmd_A3_Upgrade_End = 0xA3,
        LinkAck_A3_ApplyNewApp = 0x00, //响应0x00开始升级
        LinkAck_A3_CRC16CheckErr = 0x01, //响应0x01固件校验错误
        LinkAck_A3_ApplyTerminal = 0x02, //响应0x02升级中止

        //0xA4, 当前程序查询
        LinkCmd_A4_CheckRegion = 0xA4,
        LinkAck_A4_InApp = 0x01,
        LinkAck_A4_InBootloader = 0x02,


		LinkCmd_AF_Log = 0xAF, //log
    }

    public class link_msg_t
    {
		public UInt32 tick; //打印log时用
		public link_addr_t LinkAddr = link_addr_t.LinkAddr_BMS;
        public link_cmd_ack_t LinkCmd;
        public UInt16 data_len = 0;
        public byte[] data;

        //--------------------------------------------------------------------------------------
        //Rx

        //解析串口数据流, 返回解析到的msg
        static mt_link_frame_rx_t rx_make = new mt_link_frame_rx_t();
        static public link_msg_t Parse(byte data_in)
        {
            return rx_make.link_make_frame(data_in);
        }

        static public List<link_msg_t> Parse(byte[] data)
        {
            List<link_msg_t> list = new List<link_msg_t>();
            for (int i = 0; i < data.Length; i++)
            {
                link_msg_t msg = Parse(data[i]);
                if (msg != null)
                    list.Add(msg);
            }
            return list;
        }



        public class A1_ACK
        {
            public byte ret_code;
            public UInt16 pack_size;
            public UInt16 pack_num;
            public byte location;
        }

        public A1_ACK Get_A1_ACK()
        {
            A1_ACK ack = null;

            if (this.LinkCmd == link_cmd_ack_t.LinkCmd_A1_Upgrade_Start && this.data_len >= 6)
            {
                ack = new A1_ACK();
                ack.ret_code = this.data[0];
                ack.pack_size = (UInt16)(((UInt16)this.data[1] << 0) + ((UInt16)this.data[2] << 8));
                ack.pack_num = (UInt16)(((UInt16)this.data[3] << 0) + ((UInt16)this.data[4] << 8));
                ack.location = this.data[5];
            }
            return ack;
        }

        public class A2_ACK
        {
            public UInt16 id;
            public byte ret_code;
        }
        public A2_ACK Get_A2_ACK()
        {
            A2_ACK ack = null;

            if (this.LinkCmd == link_cmd_ack_t.LinkCmd_A2_Upgrade_Data && this.data_len >= 3)
            {
                ack = new A2_ACK();
                ack.id = (UInt16)(((UInt16)this.data[0] << 0) + ((UInt16)this.data[1] << 8));
                ack.ret_code = this.data[2];
            }
            return ack;
        }

        public class A3_ACK
        {
            public byte ret_code;
        }

        public A3_ACK Get_A3_ACK()
        {
            A3_ACK ack = null;
            if (this.LinkCmd == link_cmd_ack_t.LinkCmd_A3_Upgrade_End && this.data_len >= 1)
            {
                ack = new A3_ACK();
                ack.ret_code = this.data[0];
            }
            return ack;
        }

        public class A4_ACK
        {
            public byte location;
        }
        public A4_ACK Get_A4_ACK()
        {
            A4_ACK ack = null;
            if (this.LinkCmd == link_cmd_ack_t.LinkCmd_A2_Upgrade_Data && this.data_len >= 1)
            {
                ack = new A4_ACK();
                ack.location = this.data[0];
            }
            return ack;
        }

        //--------------------------------------------------------------------------------------
        //Tx

        //将消息转成帧, 以便串口发出
        public byte[] ToFrame()
        {
            byte[] frame = new byte[this.data_len + 9];
            int index = 0;
            frame[index++] = 0xAA;
            frame[index++] = 0x55;
            frame[index++] = 0xBB;
            frame[index++] = (byte)this.LinkAddr;
			frame[index++] = (byte)this.LinkCmd;
            frame[index++] = (byte)(this.data_len >> 0);
            frame[index++] = (byte)(this.data_len >> 8);
            for (int i = 0; i < this.data_len; i++)
            {
                frame[index++] = this.data[i];
            }

            UInt16 crc = crc16.GetCRCCode(frame, index);
            frame[index++] = (byte)(crc >> 0);
            frame[index++] = (byte)(crc >> 8);

            return frame;
        }

		public override string ToString()
		{
			StringBuilder ret = new StringBuilder();
			if (this.LinkCmd == link_cmd_ack_t.LinkCmd_AF_Log)
			{
				if (this.data != null)
				{
					foreach (byte item in this.data)
						ret.Append((char)item);
				}
				return ret.ToString();
			}

			byte[] frame = this.ToFrame();
			for(int i = 0; i < 32; i++)
			{
				if (i >= frame.Length)
					break;
				ret.Append(frame[i].ToString("X2") + ' ');
			}
			if (frame.Length > 32)
				ret.Append("...");

			return ret.ToString();
		}

		public static link_msg_t Create_msg_A1(byte ver_protocol, byte factory_code, UInt16 ver_hw, UInt16 ver_sw, UInt32 file_size)
        {
            link_msg_t msg = new link_msg_t();
            msg.LinkCmd = link_cmd_ack_t.LinkCmd_A1_Upgrade_Start;
            msg.data_len = 10;
            msg.data = new byte[msg.data_len];

            int index = 0;
            msg.data[index++] = ver_protocol;
            msg.data[index++] = factory_code;
            msg.data[index++] = (byte)(ver_hw >> 0);
            msg.data[index++] = (byte)(ver_hw >> 8);
            msg.data[index++] = (byte)(ver_sw >> 0);
            msg.data[index++] = (byte)(ver_sw >> 8);
            msg.data[index++] = (byte)(file_size >> 0);
            msg.data[index++] = (byte)(file_size >> 8);
            msg.data[index++] = (byte)(file_size >> 16);
            msg.data[index++] = (byte)(file_size >> 24);
            return msg;
        }

        public static link_msg_t Create_msg_A2(UInt16 id, byte[] data)
        {
            link_msg_t msg = new link_msg_t();
            msg.LinkCmd = link_cmd_ack_t.LinkCmd_A2_Upgrade_Data;
            msg.data_len = (UInt16)((data == null) ? 2 :(data.Length + 2));
            msg.data = new byte[msg.data_len];

            int index = 0;
            msg.data[index++] = (byte)(id >> 0);
            msg.data[index++] = (byte)(id >> 8);
            for (int i = 0; i < data.Length; i++)
            {
                msg.data[index++] = data[i];
            }
            return msg;
        }

        public static link_msg_t Create_msg_A3(byte action, UInt16 crc16)
        {
            link_msg_t msg = new link_msg_t();
            msg.LinkCmd = link_cmd_ack_t.LinkCmd_A3_Upgrade_End;
            msg.data_len = 3;
            msg.data = new byte[msg.data_len];

            int index = 0;
            msg.data[index++] = action;
			msg.data[index++] = (byte)(crc16 >> 0);
			msg.data[index++] = (byte)(crc16 >> 8);

            return msg;
        }

        public static link_msg_t Create_msg_A4()
        {
            link_msg_t msg = new link_msg_t();
            msg.LinkCmd = link_cmd_ack_t.LinkCmd_A4_CheckRegion;
            msg.data_len = 0;
            msg.data = new byte[msg.data_len];

            return msg;
        }
    }


}
